To make code easy to read and maintain, we should follow some best practices.
In this article, we’ll look at some best practices we should follow to make everyone’s lives easier.
Non-Null Assertion
We can add a non-null assertion operator to make sure that sone object is never null
or undefined
.
It’s denoted by an exclamation mark.
For instance, we can write:
function bar(foo: Foo | undefined) {
foo!.doSomething();
}
We have a foo
parameter which can be undefined
.
So to make sure it isn’t undefined
, we use the !.
operator to make sure that it’s not undefined
.
No Magic Numbers
We should make sure that we don’t have magic numbers in our code.
They are hard to understand from the code.
Also, we’ve to change in multiple places if we have to change them.
Therefore, instead of writing:
const metersInFeet = meters * 3.28
We write:
const METER_IN_FEET = 3.28;
const metersInFeet = meters * METER_IN_FEET;
We create a constant and use that so everyone knows what it holds.
We can reference it anywhere so we only have to change it once if needed.
No Parameter Assignments
Assigning parameters is a bad idea.
It mutates the argument if it’s an object since they’re passed in by reference/
So instead of writing:
function foo(x: number) {
x = 2;
}
We write:
function foo(x: number) {
const y = 2;
}
No reference Syntax for Referencing Other TypeScript Files
Before ES modules are incorporated into TypeScript, we used the reference
syntax to reference other TypeScript files.
However, now we can use ES6 modules instead.
For instance, instead of writing:
/// <reference path='./foo'>
We convert our TypeScript types to modules and write:
import baz from foo;
No var require
With TypeScript, we can use the import
syntax to require CommonJS modules.
So instead of writing:
var module = require("module");
Instead, we write:
`import` module `= require('`module`');`
We use require
, but with import
on the left side.
It’s part of the language and TypeScript will compile this properly.
Arrow Functions
Whenever we don’t need this
in our functions, we should use arrow functions.
They’re shorter and won’t bind to a value of this
.
So instead of writing:
const foo = function() {
doWork();
};
We write:
const foo = () => {
doWork();
};
Use the for-of Loop
The for-of loop is the most versatile kind of loop in javaScript.
It lets us loop through any kind of JavaScript iterable object.
The only thing it can’t do is get us the index.
So when the index isn’t needed, we can write:
for (const a of arr) {
const(a);
}
Use async with Promises
Functions that returns a promise should be marked async
.
This way, we make sure that they return a promise with the resolved value.
For instance, instead of writing:
const foo = () => {
return mypromise
.then((val) => {
return val;
})
}
We write:
const foo = async () => {
const val = await myPromise;
return val;
}
They both return a promise with resolved value val
.
Add Data Type Definitions to Our Code
We should add data type definitions to our code.
They can be added to variables, parameters, and as return types.
This will let us take advantage of data type checking and make them easier to read.
For instance, instead of writing:
function subtract(x, y) {
return x - y;
}
We write:
function subtract(x: number, y: number): number {
return x - y;
}
We specified the data types of the parameters and the return type.
Unifying Signatures
If we have 2 function signature overloads that can be combined into one with a union or rest parameter, we should do so.
For instance, if we have:
function foo(a: number);
function foo(a: string) {
console.log(a);
}
We can write:
function foo(a: number | string) {
console.log(a);
}
instead.
Conclusion
We can add non-null assertions to make sure something isn’t null
or undefined
.
Also, we shouldn’t use magic numbers.
And we can clean up our code with async
and await
, for-of loops, and combining signatures.